﻿using System;
using System.Collections;
using System.Threading;
using LyricsEngine;

[assembly: CLSCompliant(true)]
namespace LyricsEngine
{
    public class LyricsController : IDisposable, ILyricsEngine
    {

        #region Fields
        private ILyricForm _Form;

        private int _noOfLyricsToSearch;
        private int _noOfLyricsSearched;
        private int _noOfLyricsFound;
        private int _noOfLyricsNotFound;

        private int _noOfCurrentSearches;

        private bool _StopSearches;

        private bool _allowAllToComplete;
        private bool _automaticUpdate;

        ArrayList threadList = new ArrayList();
        
        // Main thread sets this event to stop LyricController
        ManualResetEvent _EventStop_LyricController;

        // The LyricController sets this when all lyricSearch threads have been aborted
        ManualResetEvent _EventStopped_LyricController;

        private string[] _lyricsSites;
        private string[] _findArray;
        private string[] _replaceArray;

        #endregion

        #region Constructor

        public void Initialize(ILyricForm mainForm,
                                ManualResetEvent eventStopThread,
                                string[] lyricSites,
                                bool allowAllToComplete, bool automaticUpdate,
                                string find, string replace)
        {

            this._Form = mainForm;
            this._allowAllToComplete = allowAllToComplete;
            this._automaticUpdate = automaticUpdate;

            _noOfLyricsToSearch = 1;
            _noOfLyricsSearched = 0;
            _noOfLyricsFound = 0;
            _noOfLyricsNotFound = 0;
            _noOfCurrentSearches = 0;

            ArrayList sitesArrayList = new ArrayList();

            // If search all, then include all
            foreach (string site in lyricSites)
            {
                if (Setup.IsMember(site))
                {
                    sitesArrayList.Add(site);
                }
            }
            this._lyricsSites = (string[])sitesArrayList.ToArray(typeof(string));


            LyricSearch.LyricsSites = _lyricsSites;

            _EventStop_LyricController = eventStopThread;
            _EventStopped_LyricController = new ManualResetEvent(false);

            LyricSearch.Abort = false;

            if (!string.IsNullOrEmpty(find) && !string.IsNullOrEmpty(replace))
            {
                if (find != "")
                {
                    _findArray = find.Split(',');
                    _replaceArray = replace.Split(',');
                }
            }
        }

        #endregion


        public void Run()
        {
            // check if thread is cancelled
            while (true)
            {
                Thread.Sleep(100);

                // check if thread is cancelled
                if (_EventStop_LyricController.WaitOne())
                {
                    // clean-up operations may be placed here
                    for (int i=0; i<threadList.Count; i++)
                    {
                        ((Thread)threadList[i]).Abort();
                    }

                    bool stillThreadsAlive = (threadList.Count > 0 ? true : false);
                    while (stillThreadsAlive)
                    {
                        for (int i = 0; i < threadList.Count; i++)
                        {
                            stillThreadsAlive = false; ;
                            if (((Thread)threadList[i]).IsAlive)
                                stillThreadsAlive = true;
                        }
                    }

                    _EventStopped_LyricController.Set();
                    break;
                }
            }
        }


        public void Dispose()
        {
            // clean-up operations may be placed here
            for (int i = 0; i < threadList.Count; i++)
            {
                ((Thread)threadList[i]).Abort();
            }

            bool stillThreadsAlive = true;
            while (stillThreadsAlive)
            {
                for (int i = 0; i < threadList.Count; i++)
                {
                    stillThreadsAlive = false;
                    if (((Thread)threadList[i]).IsAlive)
                        stillThreadsAlive = true;
                }

                if (threadList.Count == 0)
                {
                    stillThreadsAlive = false;
                }
            }
            FinishThread("", "", "The search has ended.", "");
        }

        public void AddNewLyricSearch(string artist, string title, string strippedArtistName)
        {
            if (_lyricsSites.Length > 0 && !string.IsNullOrEmpty(artist) && !string.IsNullOrEmpty(title))
            {
                ++_noOfCurrentSearches;

                // create worker thread instance
                ThreadStart threadInstance = delegate
                {
                    LyricSearch lyricSearch = new LyricSearch(this, artist, title, strippedArtistName, _allowAllToComplete, _automaticUpdate);
                    lyricSearch.Run();
                };

                Thread lyricSearchThread = new Thread(threadInstance);
                lyricSearchThread.Name = "BasicSearch for " + artist + " - " + title;	// looks nice in Output window
                lyricSearchThread.IsBackground = true;
                lyricSearchThread.Start();
                threadList.Add(lyricSearchThread);
            }
            else
            {
                return;
            }
        }

        #region Internal members

        internal void UpdateString(String message, String site)
        {
            _Form.UpdateString = new Object[] { message, site };
        }

        internal void StatusUpdate(string artist, string title, string site, bool lyricFound)
        {
            if (lyricFound)
                ++_noOfLyricsFound;
            else
                ++_noOfLyricsNotFound;

            ++_noOfLyricsSearched;

            _Form.UpdateStatus = new Object[] { _noOfLyricsToSearch, _noOfLyricsSearched, _noOfLyricsFound, _noOfLyricsNotFound };

            if ((_noOfLyricsSearched >= _noOfLyricsToSearch))
            {
                FinishThread(artist, title, "All songs have been searched!", site);
            }
        }

        internal void LyricFound(String lyricStrings, String artist, String title, String site)
        {

            string cleanLyric = LyricUtil.FixLyrics(lyricStrings, _findArray, _replaceArray);

            --_noOfCurrentSearches;

            if (_allowAllToComplete || _StopSearches == false)
            {
                _Form.LyricFound = new Object[] { cleanLyric, artist, title, site };
                StatusUpdate(artist, title, site, true);
            }
        }

        internal void LyricNotFound(String artist, String title, String message, String site)
        {
            --_noOfCurrentSearches;

            if (_allowAllToComplete || _StopSearches == false)
            {
                _Form.LyricNotFound = new Object[] { artist, title, message, site };
                StatusUpdate(artist, title, site, false);
            }
        }

        #endregion

        public void FinishThread(String artist, String title, String message, String site)
        {
            _StopSearches = true;
            _EventStop_LyricController.Set();

            while (!_EventStopped_LyricController.WaitOne(Timeout.Infinite, true))
            {
                Thread.Sleep(50);
            }
            _Form.ThreadFinished = new Object[] { artist, title, message, site };
        }

        internal void ThreadException(String s)
        {
            _Form.ThreadException = s;
        }

        public bool StopSearches
        {
            get { return _StopSearches; }
            set {
                if (value == true)
                {
                    _StopSearches = true;
                    LyricSearch.Abort = true;
                    //StopTheSearchAndAbort.Invoke(this, EventArgs.Empty);
                }
                else
                {
                    _StopSearches = false;
                    LyricSearch.Abort = false;
                }
            }
        }

        public int NoOfLyricsToSearch
        {
            set { _noOfLyricsToSearch = value; }
        }

        public int NoOfCurrentSearches
        {
            get { return _noOfCurrentSearches; }
        }
    }
}
